#include <assert.h>
#include "event_loop.h"
#include "channel_map.h"

int event_loop_handle_pending_channel(struct event_loop *eloop)
{
    pthread_mutex_lock(&eloop->mutex);
    eloop->is_handle_pending = 1;

    struct channel_element *chan_elem = eloop->pending_head;
    while (chan_elem != NULL)
    {
        struct channel *chan = chan_elem->chan;
        int fd = chan->fd;
        if (chan_elem->type == 1)
        {
            event_loop_handle_pending_add(eloop, fd, chan);
        }
        else if (chan_elem->type == 2)
        {
            event_loop_handle_pending_remove(eloop, fd, chan);
        }
        else if (chan_elem->type == 3)
        {
            event_loop_handle_pending_update(eloop, fd, chan);
        }
        chan_elem = chan_elem->next;
    }
    eloop->pending_head = eloop->pending_tail = NULL;
    eloop->is_handle_pending = 0;

    pthread_mutex_unlock(&eloop->mutex);

    return 0;
}

void event_loop_channel_buffer_nlock(struct event_loop *eloop, int fd,
                                     struct channel *chan, int type)
{
    struct channel_element *chan_elem = malloc(sizeof(struct channel_element));
    chan_elem->chan = chan;
    chan_elem->type = type;
    chan_elem->next = NULL;
    // 第一个元素
    if (eloop->pending_head == NULL)
    {
        eloop->pending_head = eloop->pending_tail = chan_elem;
    }
    else
    {
        eloop->pending_tail->next = chan_elem;
        eloop->pending_tail = chan_elem;
    }
}


// 1： same thread: 0： not the same thread
int is_in_same_thread(struct event_loop *eloop)
{
    return eloop->owner_thread_id == pthread_self();
}

int event_loop_do_channel_event(struct event_loop *eloop, int fd,
                                struct channel *chan, int type)
{
    pthread_mutex_lock(&eloop->mutex);
    assert(eloop->is_handle_pending == 0);
    event_loop_channel_buffer_nlock(eloop, fd, chan, type);
    pthread_mutex_unlock(&eloop->mutex);
    if (!is_in_same_thread(eloop))
    {
        event_loop_wakeup(eloop);
    }
    else
    {
        event_loop_handle_pending_channel(eloop);
    }
    return 0;
}

int event_loop_add_channel_event(struct event_loop *eloop, int fd, struct channel *chan)
{
    return event_loop_do_channel_event(eloop, fd, chan, 1);
}

int event_loop_remove_channel_event(struct event_loop *eloop, int fd, struct channel *chan)
{
    return event_loop_do_channel_event(eloop, fd, chan, 2);
}

int event_loop_update_channel_event(struct event_loop *eloop, int fd, struct channel *chan)
{
    return event_loop_do_channel_event(eloop, fd, chan, 3);
}

int event_loop_handle_pending_add(struct event_loop *eloop, int fd, struct channel *chan)
{
    struct channel_map *map = eloop->chan_map;
    if (fd < 0)
    {
        return 0;
    }
    if (fd >= map->nentries)
    {
        if (map_make_space(map, fd, sizeof(struct channel *)) == -1)
        {
            return -1;
        }
    }
    if (map->entries[fd] == NULL)
    {
        map->entries[fd] = chan;
        struct event_dispatcher *dispatcher = eloop->dispatcher;
        dispatcher->add(eloop, chan);
        return 1;
    }
    return 0;
}

int event_loop_handle_pending_remove(struct event_loop *eloop, int fd, struct channel *chan)
{
    struct channel_map *map = eloop->chan_map;
    assert(fd == chan->fd);

    if (fd < 0)
    {
        return 0;
    }
    if (fd >= map->nentries)
    {
        return -1;
    }
    struct channel *chan2 = map->entries[fd];
    struct event_dispatcher *dispacther = eloop->dispatcher;
    int retval = 0;
    if (dispacther->del(eloop, chan2) == -1)
    {
        retval = -1;
    }
    else
    {
        retval = 1;
    }
    map->entries[fd] = NULL;
    return retval;
}

int event_loop_handle_pending_update(struct event_loop *eloop, int fd, struct channel *chan)
{
    struct channel_map *map = eloop->chan_map;
    if (fd < 0)
    {
        return 0;
    }
    if (map->entries[fd] == NULL)
    {
        return -1;
    }
    struct event_dispatcher *dispacther = eloop->dispatcher;
    dispacther->update(eloop, chan);
    return 1;
}

int channel_event_activate(struct event_loop *eloop, int fd, int revents)
{
    struct channel_map *map = eloop->chan_map;
    if (fd < 0)
    {
        return 0;
    }
    if (fd >= map->nentries)
    {
        return -1;
    }
    struct channel *chan = map->entries[fd];
    assert(fd == chan->fd);
    if (revents & EVENT_READ)
    {
        if (chan->read_callback)
        {
            chan->read_callback(chan->data);
        }
        if (chan->write_callback)
        {
            chan->write_callback(chan->data);
        }
    }
    return 0;
}

void event_loop_wakeup(struct event_loop *eloop)
{
    char one = 'a';
    ssize_t n = write(eloop->socketPair[0], &one, sizeof one);
    if (n != sizeof one)
    {
        printf("wakeup event loop thread failed\n");
    }
}

int handle_wakeup(void *data)
{
    struct event_loop *eloop = (struct event_loop *)data;
    char one;
    ssize_t n = read(eloop->socketPair[1], &one, sizeof one);
    if (n != sizeof one)
    {
        printf("handle_wakeup failed\n");
    }
    printf("waleup, %s\n", eloop->thread_name);
}

struct event_loop *event_loop_init()
{
    return event_loop_init_with_name(NULL);
}

struct event_loop *event_loop_init_with_name(char *thread_name)
{
    struct event_loop *eloop = malloc(sizeof(struct event_loop));
    pthread_mutex_init(&eloop->mutex, NULL);
    pthread_cond_init(&eloop->cond, NULL);

    if (thread_name != NULL)
    {
        eloop->thread_name = thread_name;
    }
    else
    {
        eloop->thread_name = "main thread";
    }
    eloop->quit = 0;
    eloop->chan_map = malloc(sizeof(struct channel_map));
    map_init(eloop->chan_map);

    // 设置 epoll 派发器
    eloop->dispatcher = &epoll_dispatcher;
    eloop->event_dispatcher_data = eloop->dispatcher->init(eloop);
    eloop->owner_thread_id = pthread_self();
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, eloop->socketPair) < 0)
    {
        printf("socketpair set failed\n");
    }
    eloop->is_handle_pending = 0;
    eloop->pending_head = NULL;
    eloop->pending_tail = NULL;

    struct channel *chan = channel_new(eloop->socketPair[1], EVENT_READ,
                                       handle_wakeup, NULL, eloop);
    event_loop_add_channel_event(eloop, eloop->socketPair[1], chan);
    return eloop;
}

int event_loop_run(struct event_loop *eloop)
{
    assert(eloop != NULL);
    struct event_dispatcher *dispatcher = eloop->dispatcher;
    if (eloop->owner_thread_id != pthread_self())
    {
        exit(EXIT_FAILURE);
    }
    struct timeval timeval;
    timeval.tv_sec = 1;
    while (!eloop->quit)
    {
        dispatcher->dispatch(eloop, &timeval);
        event_loop_handle_pending_channel(eloop);
    }
    return 0;
}